#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connectDigitKey(this);
    connectOpKey(this);
    connectFunctionKey(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

/**
 * @brief: Connect digit pad
*/
void MainWindow::connectDigitKey(MainWindow *window){
    for(int i=0; i<=9; i++){
        QPushButton *button = qobject_cast<QPushButton*>(window->findChild<QObject*>("pushButton_" + QString::number(i)));
        QObject::connect(button, SIGNAL(released()), window, SLOT(digitPressed()));
    }
    QObject::connect(ui->pushButton_spot, SIGNAL(released()), window, SLOT(spotPressed()));
}

void MainWindow::connectOpKey(MainWindow *window){
    QObject::connect(ui->pushButton_add, SIGNAL(released()), window, SLOT(opPressed()));
    QObject::connect(ui->pushButton_minus, SIGNAL(released()), window, SLOT(opPressed()));
    QObject::connect(ui->pushButton_times, SIGNAL(released()), window, SLOT(opPressed()));
    QObject::connect(ui->pushButton_divide, SIGNAL(released()), window, SLOT(opPressed()));

    QObject::connect(ui->pushButton_equal, SIGNAL(released()), window, SLOT(equalPressed()));
    QObject::connect(ui->pushButton_equal, SIGNAL(released()), window, SLOT(equalPressed_with3rd()));
}

void MainWindow::connectFunctionKey(MainWindow *window){
    QObject::connect(ui->pushButton_clear, SIGNAL(released()), window, SLOT(clearPressed()));
    QObject::connect(ui->pushButton_delete, SIGNAL(released()), window, SLOT(delPressed()));
}

/**
 * @brief: digit key slot function
*/
void MainWindow::digitPressed(){
    QPushButton *button = (QPushButton *)sender(); // used to catch the button signal
    //double labelnumber;
    insert_formula.push_back(button->text());
    showText();
}

/**
 * @brief the logic of inserting spot
*/
void MainWindow::spotPressed(){
    QPushButton *button = (QPushButton *)sender();
    bool already_insert_spot = false;
    if(!insert_formula.isEmpty()){
        for(int i = insert_formula.size() - 1; i>=0; i--){
            if(insert_formula[i] == SPOT) {
                already_insert_spot = true;
            }
            if (isOperatorSymbol(insert_formula[i])){
                break;
            }
        }
        if(!already_insert_spot){
            static QRegularExpression re("\\d+"); // 正则表达式模式，匹配一个或多个数字
            QRegularExpressionMatch match = re.match(insert_formula.last());
            bool isNumber = match.hasMatch();
            if (isNumber)
                insert_formula.push_back(button->text());
            else {
                insert_formula.push_back("0.");
            }
        }
    }
    else {
        insert_formula = {"0","."};
    }
    showText();
}

/**
 * @brief calculat operator slot function
*/
void MainWindow::opPressed(){
    QPushButton *button = (QPushButton *)sender();
    if (!insert_formula.isEmpty()){
        if (isOperatorSymbol(insert_formula.last())){
            insert_formula.last() = button->text();
        }
        else
            insert_formula.push_back(button->text());
    }
    else{
        if(button->text() == MINUS)
           insert_formula.push_back(button->text());
    }
    showText();
}

void MainWindow::delPressed(){
    if(!insert_formula.isEmpty()){
        insert_formula.removeLast();
    }
    showText();
}

void MainWindow::clearPressed(){
    insert_formula.clear();
    output_text = "0";
    showText();
}

/**
 * @brief calculate expression by using Qt library called "QScriptValue"
 * @remark it will appear some bugs while calculating "3.2 * 3" which is caused by the precision loss of "double"
*/
void MainWindow::equalPressed(){
    if (isOperatorSymbol(insert_formula.last())){
        insert_formula.removeLast();
    }
    input_text = qvec2qstr(insert_formula);
    QScriptValue answer = my_engine.evaluate(input_text); // calculate the formula
    QString answer_str = answer.toString();
    double answer_value = answer_str.toDouble(); // while converting to double, it loss precision
//    std::cout << answer_value << std::endl;
//    int decimalPlaces = 10; // 设定希望保留的小数位数
//    double rounded_ans = std::round(answer_value * std::pow(10, decimalPlaces)) / std::pow(10, decimalPlaces);
//    qDebug() << rounded_ans;
//    output_text = QString::number(rounded_ans);
//    output_text = QString::number(answer_value,'f',5);
    output_text = answer.toString();
//    ui->label_2->setText(output_text);
}

/**
 * @brief calculate expression by using third party called "exprtk"
*/
void MainWindow::equalPressed_with3rd(){
    if (isOperatorSymbol(insert_formula.last())){
        insert_formula.removeLast();
    }
    input_text = qvec2qstr(insert_formula);
    expression_t expression;
    parser_t parser;
    parser.compile(input_text.toStdString(),expression);
    double value = expression.value();
    QString output_str = QString::number(value, 'g', 10);
    ui->label_2->setText(output_str);
}

/**
 * @brief show text info on label and label_2
*/
void MainWindow::showText(){
    input_text = qvec2qstr(insert_formula);
    ui->label->setText(input_text);
    ui->label_2->setText(output_text);
}

/**
 * @brief convert from QVector<QString> to QString
*/
QString MainWindow::qvec2qstr(QVector<QString> &string_vec){
    QString output;
    if (!string_vec.isEmpty())
        for ( const QString &str : string_vec )
           output += str;
    else
        output = "0";
    return output;
}

bool MainWindow::isOperatorSymbol(QString &str){
    if (str==ADD || str==MINUS || str==DIVIDE || str==TIMES){
        return true;
    }
    else
        return false;
}
